# stoker_local:  Local machine definitions source for Stoker
#
# Author:   Mike Murphy (mamurph@cs.clemson.edu)
# Revision: 8 September 2008
#
#   Copyright 2007-2008 Clemson University
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
# This material is based upon work supported under a National Science
# Foundation Graduate Research Fellowship. Any opinions, findings,
# conclusions or recommendations expressed in this publication are those of
# the author(s) and do not necessarily reflect the views of the National
# Science Foundation.

import scf2, stoker_base, sys
from stoker_defines import CFG_MACHINE_DEFS, CFG_MACHINE_FILE, \
                           HOSTNAME_RESOLUTION_LOCAL, MAC_ADDRESS_LOCAL, \
                           EXIT_ERROR_CONFIG, CFG_CALLABLE_GROUPS, \
                           HOSTNAME_KEY, IPV4_KEY, MAC_KEY
#
from stoker_base import pr_warning

from stoker_messages import MSG_DROP_CIRCULAR_GROUP, MSG_DUPLICATE_MACHINE
#

class LocalMachines(stoker_base.MachineSourceBase):
   def __init__(self, stoker_config):
      stoker_base.MachineSourceBase.__init__(self, stoker_config)
      self.CLI_NAME = 'local'
      self.machines = []
      self.groups = {}
      self.load_machines_and_groups()
   def resolveTargets(self, targets):
      return resolve_targets(targets, self.groups, self.machines, \
                              self.machine_cfg)
   def getAllMachines(self):
      machines = self.machines[:]
      machines.sort()
      return machines
   def getAllGroups(self):
      groups = self.groups.keys()
      groups.sort()
      return groups
   def load_machines_and_groups(self):
      self.machine_cfg = scf2.ConfigHandler()
      if self.config.getType(CFG_MACHINE_FILE) == 'string':
         chk = self.machine_cfg.parseConfigFile(self.config[CFG_MACHINE_FILE])
         if not chk:
            sys.exit(EXIT_ERROR_CONFIG)
         if self.machine_cfg.getType(CFG_MACHINE_DEFS) == 'list':
            self.machines = load_machines(self.machine_cfg, CFG_MACHINE_DEFS, \
               [], [])
            self.groups = load_callable_groups(self.machine_cfg)
#


# Recursive machine loading function
def load_machines(machine_cfg, key, machlist, blacklist):
   if machine_cfg.getType(key) == 'list':
      pass_blacklist = blacklist[:]
      pass_blacklist.append(key)
      for item in machine_cfg[key]:
         if item not in blacklist:
            load_machines(machine_cfg, item, machlist, pass_blacklist)
         else:
            pr_warning(MSG_DROP_CIRCULAR_GROUP % (item))
   elif machine_cfg.getType(key) == 'record':
      if key not in machlist:   # dups possible
         machlist.append(key)
      else:
         pr_warning(MSG_DUPLICATE_MACHINE % (key))
   return machlist
#

def load_callable_groups(machine_cfg):
   groups = {}
   if machine_cfg.getType(CFG_CALLABLE_GROUPS) == 'list':
      for item in machine_cfg[CFG_CALLABLE_GROUPS]:
         machines = load_machines(machine_cfg, item, [], [])
         if len(machines) > 0:
            groups[item] = machines
   return groups
#

# Extra information function
def make_match(machine, machine_cfg):
   this_machine = machine_cfg.getParam(machine)
   hostname = machine
   ipv4 = this_machine.getParam(HOSTNAME_RESOLUTION_LOCAL)
   mac = this_machine.getParam(MAC_ADDRESS_LOCAL)
   match = { HOSTNAME_KEY : hostname }
   if ipv4:
      match[IPV4_KEY] = ipv4
   if mac:
      match[MAC_KEY] = mac
   return match

# Resolver function
def resolve_targets(target_list, group_dict, machine_list, machine_cfg):
   resolved = []
   unresolved = []
   for target in target_list:
      if target in group_dict:
         for machine in group_dict[target]:
            final_name = make_match(machine, machine_cfg)
            if final_name not in resolved:
               resolved.append(final_name)
      elif target in machine_list:
         final_name = make_match(target, machine_cfg)
         if final_name not in resolved:
            resolved.append(final_name)
      else:
         unresolved.append(target)
   return (resolved, unresolved)
